package myjs

import (
	"errors"
	"fmt"
	"reflect"
	"strconv"
	"strings"
)

//JSONMarshal 的化简版
func JSONMarshal(v interface{}) ([]byte, error) {
	var res string = ""
	tt := reflect.TypeOf(v)
	vv := reflect.ValueOf(v)

	res += "{"
	for i := 0; i < tt.NumField(); i++ {
		name := tt.Field(i).Name
		tagname := tt.Field(i).Tag.Get("json")
		if tagname == "" {
			res += "\"" + name + "\":"
		} else {
			if tagname == "-" {
				continue
			} else {
				f := strings.Contains(tagname, ",")
				if f == true {
					nname := ""
					nntag := ""
					dot := 0
					for z := 0; z < len(tagname); z++ {
						if string(tagname[z]) == "," {
							dot = 1
							continue
						}
						if dot == 0 {
							nname += string(tagname[z])
						} else {
							nntag += string(tagname[z])
						}
					}
					if nntag == "omitempty" {
						ff := true
						switch tt.Field(i).Type.String() {
						case "bool":
							if vv.Field(i).Bool() == false {
								ff = false
							}
						case "int", "int8", "int16", "int32", "int64":
							if vv.Field(i).Int() == 0 {
								ff = false
							}
						case "uint", "uint8", "uint16", "uint32", "uint64":
							if vv.Field(i).Uint() == 0 {
								ff = false
							}
						case "float32":
							if vv.Field(i).Float() == 0.0 {
								ff = false
							}
						case "float64":
							if vv.Field(i).Float() == 0.0 {
								ff = false
							}
						case "string":
							if vv.Field(i).String() == "" {
								ff = false
							}
						case "[]int", "[]float32", "[]float64", "[]string":
							if vv.Field(i).Len() == 0 {
								ff = false
							}
						case "map[int]int", "map[string]int", "map[string]interface {}":
							if vv.Field(i).Len() == 0 {
								ff = false
							}
						}
						if ff == false {
							continue
						}
					}
					if nname == "" {
						res += "\"" + name + "\":"
					} else {
						res += "\"" + nname + "\":"
					}

				} else {
					res += "\"" + tagname + "\":"
				}
			}
		}

		switch tt.Field(i).Type.String() {
		case "bool":
			res += dealbool(vv.Field(i).Bool())
		case "int", "int8", "int16", "int32", "int64":
			res += dealint(vv.Field(i).Int())
		case "uint", "uint8", "uint16", "uint32", "uint64":
			res += dealuint(vv.Field(i).Uint())
		case "float32":
			res += dealfloat32(vv.Field(i).Float())
		case "float64":
			res += dealfloat64(vv.Field(i).Float())
		case "string":
			res += dealstring(vv.Field(i).String())
		case "[]int", "[]float32", "[]float64", "[]string":
			switch tt.Field(i).Type.Elem().String() {
			case "int":
				res += dealintslice(vv.Field(i).Interface().([]int))
			case "float32":
				res += dealfloat32slice(vv.Field(i).Interface().([]float32))
			case "float64":
				res += dealfloat64slice(vv.Field(i).Interface().([]float64))
			case "string":
				res += dealstringslice(vv.Field(i).Interface().([]string))
			}
		case "map[int]int", "map[string]int", "map[string]interface {}":
			switch tt.Field(i).Type.String() {
			case "map[int]int":
				res += dealmapintint(vv.Field(i).Interface().(map[int]int))
			case "map[string]int":
				res += dealmapstrint(vv.Field(i).Interface().(map[string]int))
			case "map[string]interface {}":
				res += dealmapstrinterface(vv.Field(i).Interface().(map[string]interface{}))
			}
		default:
			return nil, errors.New("unsupported type encoder:" + tt.Field(i).Type.String())
		}

		if i != tt.NumField()-1 {
			res += ","
		}
	}
	if string(res[len(res)-1]) == "," {
		res = res[:len(res)-1]
	}
	res += "}"
	var ret []byte = []byte(res)
	return ret, nil
}

func dealbool(v bool) string {
	str := ""
	if v == true {
		str += "true"
	} else {
		str += "false"
	}
	return str
}

func dealstring(v string) string {
	return "\"" + v + "\""
}

func dealint(v int64) string {
	return strconv.FormatInt(v, 10)
}

func dealuint(v uint64) string {
	return strconv.FormatUint(v, 10)
}

func dealfloat32(v float64) string {
	return strconv.FormatFloat(v, 'f', -1, 32)
}

func dealfloat64(v float64) string {
	return strconv.FormatFloat(v, 'f', -1, 64)
}

func dealintslice(v []int) string {
	res := ""
	res += "["
	for k := 0; k < len(v); k++ {
		res += strconv.Itoa(v[k])
		if k != len(v)-1 {
			res += ","
		}
	}
	res += "]"
	return res
}

func dealfloat32slice(v []float32) string {
	res := ""
	res += "["
	for k := 0; k < len(v); k++ {
		res += strconv.FormatFloat(float64(v[k]), 'f', -1, 32)
		if k != len(v)-1 {
			res += ","
		}
	}
	res += "]"
	return res
}

func dealfloat64slice(v []float64) string {
	res := ""
	res += "["
	for k := 0; k < len(v); k++ {
		res += strconv.FormatFloat(v[k], 'f', -1, 64)
		if k != len(v)-1 {
			res += ","
		}
	}
	res += "]"
	return res
}

func dealstringslice(v []string) string {
	res := ""
	res += "["
	for k := 0; k < len(v); k++ {
		res += "\"" + v[k] + "\""
		if k != len(v)-1 {
			res += ","
		}
	}
	res += "]"
	return res
}

func dealmapintint(v map[int]int) string {
	res := ""
	res += "{"
	for i := range v {
		res += "\"" + strconv.FormatInt(int64(i), 10) + "\""
		res += ":"
		res += strconv.FormatInt(int64(v[i]), 10)
		res += ","
	}
	res = res[:len(res)-1]
	res += "}"
	return res
}

func dealmapstrint(v map[string]int) string {
	res := ""
	res += "{"
	for i := range v {
		res += "\"" + i + "\""
		res += ":"
		res += strconv.FormatInt(int64(v[i]), 10)
		res += ","
	}
	res = res[:len(res)-1]
	res += "}"
	return res
}

func dealmapstrinterface(v map[string]interface{}) string {
	res := ""
	res += "{"
	for i := range v {
		res += "\"" + i + "\""
		res += ":"

		switch v[i].(type) {
		case bool:
			if v[i] == true {
				res += "true"
			} else {
				res += "false"
			}
		case int:
			res += strconv.FormatInt(int64(v[i].(int)), 10)
		case string:
			res += "\"" + v[i].(string) + "\""
		}

		res += ","
	}
	res = res[:len(res)-1]
	res += "}"
	return res
}

type Example struct {
	ID    int                    `json:"id"`
	SID   int                    `json:"-"`
	MEN   bool                   `json:"Men,omitempty"`
	WOMEN bool                   `json:"Women,omitempty"`
	COLOR string                 `json:"-,omitempty"`
	MYMAP map[string]interface{} `json:",omitempty"`
}

func ExampleJsonMarshal() {
	group := Example{
		ID:    111,
		SID:   222,
		MEN:   true,
		WOMEN: false,
		COLOR: "Red",
		MYMAP: map[string]interface{}{"Address": "深圳", "Phone": "12345678911", "Salary": 10000},
	}
	js, err := JSONMarshal(group)
	if err == nil {
		fmt.Println(string(js))
	}
}
